#ifndef _M_TCP_H_
#define _M_TCP_H_
#include<iostream>
#include<string>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<signal.h>
using namespace std;

#define LISTEN_MAX 5
class TcpSocket{
    private:
        int _sockfd;
    public:
        int Fd()
        {
            return _sockfd;
        }
        TcpSocket():_sockfd(-1){}
        TcpSocket(int fd):_sockfd(fd){}
        ~TcpSocket(){ /*Close();*/}
        bool Available(){ return _sockfd!=-1;}
        int Fd()const {return _sockfd;}
        void reuseAddr(){
            int opt=1;
            //地址重用
            setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,(void*)&opt,sizeof(opt));
        }
        void nonBlock(){
            int flag=fcntl(_sockfd,F_GETFL);
            fcntl(_sockfd,F_SETFL,flag|O_NONBLOCK);
        }
        bool Socket(){
            _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            if(_sockfd<0){
                perror("socket error");
                return false;
            }
            return true;
           
        }
        bool Bind(const string &ip,uint16_t port){
            struct sockaddr_in addr;
            addr.sin_family=AF_INET;
            addr.sin_port=htons(port);
            addr.sin_addr.s_addr=inet_addr(ip.c_str());
            socklen_t addrlen=sizeof(struct sockaddr_in);
            int ret=bind(_sockfd,(struct sockaddr*)&addr,addrlen);
            if(ret<0){
                perror("bind error");
                return false;
            }
            return true;
        }
        bool Listen(int backlog=LISTEN_MAX){
            int ret=listen(_sockfd,backlog);
            if(ret<0){
                perror("listen error");
                return false;
            }
            return true;
        }
        TcpSocket Accept(string *peer_ip=NULL,uint16_t *peer_port=NULL){
            struct sockaddr_in addr;
            socklen_t len=sizeof(struct sockaddr_in);
            TcpSocket new_socket;
            int newfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
            if(newfd<0){
                perror("accept error");
                return new_socket;
            }
            new_socket._sockfd=newfd;
            if(peer_ip!=NULL){
                *peer_ip=inet_ntoa(addr.sin_addr);
            }
            if(peer_port!=NULL){
                *peer_port=ntohs(addr.sin_port);
            }
            new_socket.nonBlock();
            return new_socket;

        }
        bool Recv(string *body,size_t len=4096){
            body->resize(len);
            size_t total_len=0;
            while(1){
                int ret=recv(_sockfd,&(*body)[total_len],len,0);
                if(ret<=0){
                    if(errno==EAGAIN|errno==EWOULDBLOCK){
                    //当前接收数据的时候，缓冲区无数据
                        return true;
                    }
                    if(errno==EINTR){
                    //当前操作被信号打断，则重新上去接收
                        continue;
                    }
                    perror("recv error");
                    return false;
                }
                    total_len+=ret;
            }
                    body->resize(total_len);
                    return true;
        } 
        // ssize_t Send(const string &body){
        //     ssize_t ret=send(_sockfd,body.c_str(),body.size(),0);
        //     if(ret<0){
        //         perror("send error");
        //         return ret;
        //     }
        //     return ret;
        // }
        bool Send(const string &body){
            ssize_t ret=send(_sockfd,&body[0],body.size(),0);
            if(ret<=0){
                perror("send error");
                return false;
                }
            return true;
        }
        bool Connect(const string &ip,uint16_t port){
            struct sockaddr_in addr;
            addr.sin_family=AF_INET;
            addr.sin_port=htons(port);
            addr.sin_addr.s_addr=inet_addr(ip.c_str());
            socklen_t addrlen=sizeof(struct sockaddr_in);
            int ret=connect(_sockfd,(struct sockaddr*)&addr,addrlen);
            if(ret<0){
                perror("connect error");
                return false;
            }
            return true;
        }
        bool Close(){
            if(_sockfd<0)
            return false;
            close(_sockfd);
            _sockfd=-1;
            return true;
        }
};

class NetInit{
    public:
        NetInit(){
            signal(SIGPIPE,SIG_IGN);
        }
};
static NetInit g_init;
#endif
